package GameTest;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;

import utility.Camera;
import utility.OBJLoader;
import utility.ShaderLoader;




public class GameTestMain {
	
	private static int shaderProgram, bunnyhighres, diffuseModifierUniform;
	
	public static final String MODEL_LOCATION = "res/bunnyhighres.obj";
	public static final String VERTEX_SHADER_LOCATION = "res/specular_lighting.vert";
	public static final String FRAGMENT_SHADER_LOCATION = "res/specular_lighting.frag";
	
	boolean vsync;

	public static void main(String[] args) throws LWJGLException {
		try {
			Display.setDisplayMode(new DisplayMode(620,480));
			Display.setVSyncEnabled(true);
			Display.setTitle("GameTest");
			Display.create();
		} catch (LWJGLException e) {
			System.err.println("The display wasn't initialized correctly. :(");
			Display.destroy();
			System.exit(1);
		}

		Camera cam = new Camera((float) Display.getWidth()
				/ (float) Display.getHeight(), -2.19f, 1.36f, 11.45f);
		cam.setFov(60);
		cam.applyPerspectiveMatrix();

		setUpShaders();
		setUpLighting();
		setUpDisplayLists();

		while (!Display.isCloseRequested()) {
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			cam.applyModelviewMatrix(true);
			
			glUseProgram(shaderProgram);
			glUniform1f(diffuseModifierUniform, 1.5f);
			glCallList(bunnyhighres);
			glUseProgram(0);

			cam.processMouse(1, 80, -80);
			cam.processKeyboard(16, 0.003f, 0.003f, 0.003f);

			if (Mouse.isButtonDown(0))
				Mouse.setGrabbed(true);
			else if (Mouse.isButtonDown(1))
				Mouse.setGrabbed(false);


	        boolean KeyEsc = Keyboard.isKeyDown(Keyboard.KEY_ESCAPE);
	        
	        if (KeyEsc)
	        	Display.destroy();
	    	while (Keyboard.next()) {
			    if (Keyboard.getEventKeyState()) {
			        if (Keyboard.getEventKey() == Keyboard.KEY_RSHIFT) {
			        	setDisplayMode(800, 600, !Display.isFullscreen());
			       
			        }
			    }
			}
			
			Display.update();
			Display.sync(60);
		}
		glDeleteProgram(shaderProgram);
		glDeleteLists(bunnyhighres, 1);
		Display.destroy();
		System.exit(0);
	}

	public static void setDisplayMode(int width, int height, boolean fullscreen) {
		 
		if ((Display.getDisplayMode().getWidth() == width) && 
			(Display.getDisplayMode().getHeight() == height) && 
			(Display.isFullscreen() == fullscreen)) {
			return;
		}
 
		try {
			DisplayMode targetDisplayMode = null;
 
			if (fullscreen) {
				DisplayMode[] modes = Display.getAvailableDisplayModes();
				int freq = 0;
 
				for (int i=0;i<modes.length;i++) {
					DisplayMode current = modes[i];
 
					if ((current.getWidth() == width) && (current.getHeight() == height)) {
						if ((targetDisplayMode == null) || (current.getFrequency() >= freq)) {
							if ((targetDisplayMode == null) || (current.getBitsPerPixel() > targetDisplayMode.getBitsPerPixel())) {
								targetDisplayMode = current;
								freq = targetDisplayMode.getFrequency();
							}
						}

						if ((current.getBitsPerPixel() == Display.getDesktopDisplayMode().getBitsPerPixel()) &&
						    (current.getFrequency() == Display.getDesktopDisplayMode().getFrequency())) {
							targetDisplayMode = current;
							break;
						}
					}
				}
			} else {
				targetDisplayMode = new DisplayMode(width,height);
			}
 
			if (targetDisplayMode == null) {
				System.out.println("Failed to find value mode: "+width+"x"+height+" fs="+fullscreen);
				return;
			}
 
			Display.setDisplayMode(targetDisplayMode);
			Display.setFullscreen(fullscreen);
 
		} catch (LWJGLException e) {
			System.out.println("Unable to setup mode "+width+"x"+height+" fullscreen="+fullscreen + e);
		}
	}
 

	private static void setUpDisplayLists() {
		try {
			bunnyhighres = OBJLoader.createDisplayList(OBJLoader.loadModel(new File(MODEL_LOCATION)));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			glDeleteProgram(shaderProgram);
			glDeleteLists(bunnyhighres, 1);
			Display.destroy();
			System.exit(1);
		} catch (IOException e) {
			e.printStackTrace();
			glDeleteProgram(shaderProgram);
			glDeleteLists(bunnyhighres, 1);
			Display.destroy();
			System.exit(1);
		}
	}

	private static FloatBuffer asFloatBuffer(float... values) {
		FloatBuffer buffer = BufferUtils.createFloatBuffer(values.length);
		buffer.put(values);
		buffer.flip();
		return buffer;
	}

	private static void setUpLighting() {
		glShadeModel(GL_SMOOTH);
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_LIGHTING);
		glEnable(GL_LIGHT0);
		glLightModel(GL_LIGHT_MODEL_AMBIENT, asFloatBuffer(new float[] {0.05f, 0.05f, 0.05f, 1f}));
		glLight(GL_LIGHT0, GL_POSITION, asFloatBuffer(new float[] { 0,0,0, 1 }));
		glEnable(GL_CULL_FACE);
		glCullFace(GL_BACK);
		glEnable(GL_COLOR_MATERIAL);
		glColorMaterial(GL_FRONT, GL_DIFFUSE);
	}

	private static void setUpShaders() {
		shaderProgram = ShaderLoader.loadShaderPair(VERTEX_SHADER_LOCATION, FRAGMENT_SHADER_LOCATION);
		diffuseModifierUniform = glGetUniformLocation(shaderProgram, "diffuseIntensityModifier");
	}
}


